/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.servlet.book.provider;

import java.io.IOException;
import java.util.HashMap;
import java.util.Stack;
import javax.servlet.ServletException;
import offset.nodes.server.servlet.book.Book;
import offset.nodes.server.servlet.book.NameBuilder;
import offset.nodes.server.servlet.book.content.BookHyperlink;
import offset.nodes.server.servlet.book.content.Page;

/**
 *
 * @author Walter L�tgenau
 */
public class PageIterator {

    /**
     * Holds a html page and the child index while iterating
     * its children.
     */
    class HtmlPageEntry {

        int index;
        Page page;
        boolean resolved = true;

        public HtmlPageEntry(Page page) {
            this.page = page;
            this.index = 0;
        }

        public Page getPage() {
            return page;
        }

        public int getIndex() {
            return index;
        }

        public int incrementIndex() {
            int result = index;
            index++;
            
            return result;
        }

        public boolean isResolved() {
            return resolved;
        }

        public void setResolved(boolean resolved) {
            this.resolved = resolved;
        }
    }

    Stack<HtmlPageEntry> pages = new Stack<HtmlPageEntry>();
    HashMap<String,HtmlPageEntry> references = new HashMap<String,HtmlPageEntry>();
    PageProvider pageProvider;
    Book book;
    NodeProvider hyperlinkTester;
    int maxLevel;
    boolean followLinks;

    /**
     * Constructor for the page iterator, that iterates through
     * the pages of the book.
     *
     * @param root The root page
     * @param pageProvider The provider to obtain more pages
     * @param maxLevel The maximum level of child pages
     * @throws ServletException
     * @throws IOException
     */
    public PageIterator(Book book, Page root, PageProvider pageProvider, NodeProvider hyperlinkTester, int maxLevel, boolean followLinks) throws Exception {
        this.maxLevel = maxLevel;
        this.pageProvider = pageProvider;
        this.hyperlinkTester = hyperlinkTester;
        this.book = book;
        this.followLinks = followLinks;
        addPage(root);
    }

    /**
     * Is there a next page in the book?
     * @return
     */
    public boolean hasNextPage() {
        for (int i = pages.size() - 1; i >= 0; i--) {
            HtmlPageEntry entry = pages.get(i);
            if (i < maxLevel && entry.getIndex() < entry.getPage().getSublinks().size())
                return true;
        }

        return false;
    }

    /**
     * Return the next page of the book
     *
     * @return
     * @throws ServletException
     * @throws IOException
     */
    public Page nextPage() throws Exception {
        if (pages.size() == 0)
            return null;

        HtmlPageEntry current = pages.peek();
        if (current.index < current.getPage().getSublinks().size() && (pages.size() - 1 < maxLevel))
            return down();
        else {
            HtmlPageEntry entry = pages.pop();
            if (pages.size() >= maxLevel && entry.getPage().getSublinks().size() > 0)
                entry.setResolved(false); // no sublinks have been resolved
        }

        return nextPage();
    }

    /**
     * Go one level down while creating the book
     * 
     * @return
     * @throws ServletException
     * @throws IOException
     */
    protected Page down() throws Exception {
        HtmlPageEntry current = pages.peek();
        BookHyperlink link = current.getPage().getSublinks().get(current.incrementIndex());
        if (!references.containsKey(link.getHref()))
            addPage(new Page(book, pages.size() + 1, link.getHref(), pageProvider.getHtmlByUrl(link.getHref()), hyperlinkTester, followLinks, link.getType()));
        else {
            HtmlPageEntry entry = references.get(link.getHref());
            //if (!entry.isResolved()) // not yet resolved. Give it one more chance
            Page clonePage = (Page) entry.getPage().clone();
            clonePage.setLevel(pages.size() + 1);
            addPage(clonePage);
        }

        return pages.peek().getPage();
    }

    /**
     * Add a page to the internal collection.
     * 
     * @param page
     * @throws Exception
     */
    protected void addPage(Page page) throws Exception {
        HtmlPageEntry entry = new HtmlPageEntry(page);
        
        references.put(page.getRepositoryRef(), entry);
        pages.push(entry);
    }
}
